package com.xframe.xdal.core.handler;

import com.xframe.xdal.core.constant.RunOpportunity;
import com.xframe.xdal.core.domain.XDynamicModel;
import com.xframe.xdal.core.model.IModel;
import lombok.extern.log4j.Log4j2;

import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.Collection;
import java.util.Map;


/**
 * 功能描述: 基于JDBC的查询处理器，将RS返回成LIST对象
 *
 * @author xufeng
 * @version 1.0
 * @date 2023-03-20
 */
@Log4j2
public class BeanListHandler<E,T> implements ResultSetHandler<E> {


    /**
     * 设置的参数对象
     */
    private BeanHandlerOptions beanHandlerOptions;

    /**
     * 类型 ResultHandler
     */
    private final Class<T> elementBeanType;





    public static <E,T> BeanListHandler<E,T> create(Class<E> beanType,BeanHandlerOptions beanHandlerOptions){
        return new BeanListHandler(beanType,beanHandlerOptions);
    }

    public BeanListHandler(Class<T> beanType, BeanHandlerOptions beanHandlerOptions){
        this.elementBeanType = beanType;
        this.beanHandlerOptions = beanHandlerOptions;
    }

    private E newInstance() throws InstantiationException, IllegalAccessException {

        Object list = this.beanHandlerOptions.getCollectionInstance();
        return (E) list;
    }


    public E handle(ResultSet resultSet) {
        try {
             if (resultSet == null) {
                return null;
            }
            ResultSetMetaData meta = resultSet.getMetaData();
            int columnCount = meta.getColumnCount();
            E list = newInstance();
            T lastBean = null;
            Object lastKey = null;
            while (resultSet.next()) {
                //T bean = lastBean == null?elementBeanType.newInstance():lastBean;
                T bean = null;
                boolean isGetKeyVal = false;    // 是否是取第一列  第一列是主键
                boolean isRepeatData = false;   // 是否重复数据
                for (int colIndex = 1; colIndex < columnCount + 1; colIndex++) {
                    if(resultSet.getObject(colIndex) != null && beanHandlerOptions.getHandlerColumnMapManage().containsKey(colIndex)) {
                        if(!isGetKeyVal){
                            Object firstVal = beanHandlerOptions.getHandlerColumnMapManage().getValueTaking(resultSet,colIndex);
                            if(lastKey != null && lastKey.equals(firstVal)){
                                // 主键重复
                                isRepeatData = true;
                                bean = lastBean;
                            }else {
                                beanHandlerOptions.getHandlerColumnMapManage().init();
                                bean = elementBeanType.newInstance();
                                if(bean instanceof IAfterConstruction){
                                    afterConstruction(((IAfterConstruction)bean));
                                }
                                if(lastBean != null){
                                    beanHandlerOptions.getAutoProcRuntime().autoFill(lastBean,beanHandlerOptions.getTableInfo(), RunOpportunity.R);
                                    if(lastBean instanceof IBeanInit){
                                        initBean(((IBeanInit)lastBean));
                                    }
                                }
                            }
                            lastKey = firstVal;
                            isGetKeyVal = true;
                        }
                        beanHandlerOptions.getHandlerColumnMapManage().setBean(bean);
                        if(!isRepeatData){
                            Object val = beanHandlerOptions.getHandlerColumnMapManage().getValueTaking(resultSet,colIndex);
                            beanHandlerOptions.getHandlerColumnMapManage().setValueStrategy(val,colIndex);
                        }else {
                            beanHandlerOptions.getHandlerColumnMapManage().setValueListStrategy(resultSet,colIndex);
                        }
                    }
                }
                beanHandlerOptions.getHandlerColumnMapManage().setLineEnd(beanHandlerOptions.getAutoProcRuntime());
                if(!isRepeatData) {
                    lastBean = bean;

                    // 添加过滤器逻辑
                    if(this.execBeanListHandlerCallBack(bean)){
                        addTo(list,bean);
                    }
                }
            }
            if(lastBean != null){
                beanHandlerOptions.getAutoProcRuntime().autoFill(lastBean,beanHandlerOptions.getTableInfo(), RunOpportunity.R);
                if(lastBean instanceof IBeanInit){
                    initBean(((IBeanInit)lastBean));
                }
            }
            return list;
        } catch (Exception exception) {
            exception.printStackTrace();
            //log.error("exception={}",exception);
            return null;
        }
    }

    private void addTo(E list,T bean){
        if(list instanceof Collection){
            ((Collection)list).add(bean);
        }
        if(list instanceof Map){
            if(this.beanHandlerOptions.getModelMapKey() != null){
                ((Map) list).put(this.beanHandlerOptions.getModelMapKey().getMapkey(bean), bean);
            }
            else if(bean instanceof IModel){
                ((Map) list).put(((IModel)bean).getMapkey(), bean);
            }else {
                ((Map) list).put(bean.hashCode(), bean);
            }
        }
    }

    protected void afterConstruction(IAfterConstruction bean){
        try{
            bean.build();
        }catch (Exception exception){
            exception.printStackTrace();
        }
    }


    protected void initBean(IBeanInit bean){
        try{
            bean.init();
        }catch (Exception exception){
            exception.printStackTrace();
        }
    }


    protected boolean execBeanListHandlerCallBack(T bean){
        if(beanHandlerOptions.getBeanListHandlerCallBack() == null){
            return true;
        }
        try {
            return beanHandlerOptions.getBeanListHandlerCallBack().filter(bean);
        }catch (Exception exception){
            exception.printStackTrace();
            return true;
        }

    }
}
